winsafe\gui\native_controls/edit.rs
1use std::any::Any;
2use std::marker::PhantomPinned;
3use std::pin::Pin;
4use std::sync::Arc;
5
6use crate::co;
7use crate::decl::*;
8use crate::gui::{events::*, privs::*, *};
9use crate::msg::*;
10use crate::prelude::*;
11
12struct EditObj {
13 base: BaseCtrl,
14 events: EditEvents,
15 _pin: PhantomPinned,
16}
17
18native_ctrl! { Edit: EditObj => EditEvents;
19 /// Native
20 /// [edit](https://learn.microsoft.com/en-us/windows/win32/controls/about-edit-controls)
21 /// (text box) control.
22}
23
24impl Edit {
25 /// Instantiates a new `Edit` object, to be created on the parent window
26 /// with
27 /// [`HWND::CreateWindowEx`](crate::prelude::user_Hwnd::CreateWindowEx).
28 ///
29 /// # Panics
30 ///
31 /// Panics if the parent window was already created – that is, you cannot
32 /// dynamically create an `Edit` in an event closure.
33 ///
34 /// # Examples
35 ///
36 /// ```no_run
37 /// use winsafe::{self as w, prelude::*, gui};
38 ///
39 /// let wnd: gui::WindowMain; // initialized somewhere
40 /// # let wnd = gui::WindowMain::new(gui::WindowMainOpts::default());
41 ///
42 /// let txt = gui::Edit::new(
43 /// &wnd,
44 /// gui::EditOpts {
45 /// position: gui::dpi(10, 10),
46 /// width: gui::dpi_x(120),
47 /// ..Default::default()
48 /// },
49 /// );
50 /// ```
51 #[must_use]
52 pub fn new(parent: &(impl GuiParent + 'static), opts: EditOpts) -> Self {
53 let ctrl_id = auto_id::set_if_zero(opts.ctrl_id);
54 let new_self = Self(Arc::pin(EditObj {
55 base: BaseCtrl::new(ctrl_id),
56 events: EditEvents::new(parent, ctrl_id),
57 _pin: PhantomPinned,
58 }));
59
60 let self2 = new_self.clone();
61 let parent2 = parent.clone();
62 parent
63 .as_ref()
64 .before_on()
65 .wm(parent.as_ref().wnd_ty().creation_msg(), move |_| {
66 self2.0.base.create_window(
67 opts.window_ex_style,
68 "EDIT",
69 Some(&opts.text),
70 opts.window_style | opts.control_style.into(),
71 opts.position.into(),
72 SIZE::new(opts.width, opts.height),
73 &parent2,
74 )?;
75 ui_font::set(self2.hwnd())?;
76 parent2
77 .as_ref()
78 .add_to_layout(self2.hwnd(), opts.resize_behavior)?;
79 Ok(0) // ignored
80 });
81
82 new_self.default_message_handlers(parent);
83 new_self
84 }
85
86 /// Instantiates a new `Edit` object, to be loaded from a dialog resource
87 /// with [`HWND::GetDlgItem`](crate::prelude::user_Hwnd::GetDlgItem).
88 ///
89 /// # Panics
90 ///
91 /// Panics if the parent dialog was already created – that is, you cannot
92 /// dynamically create an `Edit` in an event closure.
93 #[must_use]
94 pub fn new_dlg(
95 parent: &(impl GuiParent + 'static),
96 ctrl_id: u16,
97 resize_behavior: (Horz, Vert),
98 ) -> Self {
99 let new_self = Self(Arc::pin(EditObj {
100 base: BaseCtrl::new(ctrl_id),
101 events: EditEvents::new(parent, ctrl_id),
102 _pin: PhantomPinned,
103 }));
104
105 let self2 = new_self.clone();
106 let parent2 = parent.clone();
107 parent.as_ref().before_on().wm_init_dialog(move |_| {
108 self2.0.base.assign_dlg(&parent2)?;
109 parent2
110 .as_ref()
111 .add_to_layout(self2.hwnd(), resize_behavior)?;
112 Ok(true) // ignored
113 });
114
115 new_self.default_message_handlers(parent);
116 new_self
117 }
118
119 fn default_message_handlers(&self, parent: &(impl GuiParent + 'static)) {
120 let self2 = self.clone();
121 let parent2 = parent.clone();
122 parent
123 .as_ref()
124 .before_on()
125 .wm_command(self.ctrl_id(), co::EN::CHANGE, move || {
126 // EN_CHANGE is first sent to the control before CreateWindowEx()
127 // returns, so if the user handles EN_CHANGE, the Edit HWND won't be
128 // set yet. So we set the HWND here.
129 if *self2.hwnd() == HWND::NULL {
130 let hctrl = parent2.as_ref().hwnd().GetDlgItem(self2.ctrl_id())?;
131 self2.0.base.set_hwnd(hctrl);
132 }
133 Ok(())
134 });
135 }
136
137 /// Hides any balloon tip by sending an
138 /// [`em::HideBalloonTip`](crate::msg::em::HideBalloonTip) message.
139 pub fn hide_balloon_tip(&self) -> SysResult<()> {
140 unsafe { self.hwnd().SendMessage(em::HideBalloonTip {}) }
141 }
142
143 /// Limits the number of characters that can be type by sending an
144 /// [`em::SetLimitText`](crate::msg::em::SetLimitText) message.
145 pub fn limit_text(&self, max_chars: Option<u32>) {
146 unsafe {
147 self.hwnd().SendMessage(em::SetLimitText { max_chars });
148 }
149 }
150
151 /// Sets the selection range of the text by sending an
152 /// [`em::SetSel`](crate::msg::em::SetSel) message.
153 ///
154 /// # Examples
155 ///
156 /// Selecting all text in the control:
157 ///
158 /// ```no_run
159 /// use winsafe::{self as w, prelude::*, gui};
160 ///
161 /// let my_edit: gui::Edit; // initialized somewhere
162 /// # let wnd = gui::WindowMain::new(gui::WindowMainOpts::default());
163 /// # let my_edit = gui::Edit::new(&wnd, gui::EditOpts::default());
164 ///
165 /// my_edit.set_selection(0, -1);
166 /// ```
167 ///
168 /// Clearing the selection:
169 ///
170 /// ```no_run
171 /// use winsafe::gui;
172 ///
173 /// let my_edit: gui::Edit; // initialized somewhere
174 /// # let wnd = gui::WindowMain::new(gui::WindowMainOpts::default());
175 /// # let my_edit = gui::Edit::new(&wnd, gui::EditOpts::default());
176 ///
177 /// my_edit.set_selection(-1, -1);
178 /// ```
179 pub fn set_selection(&self, start: i32, end: i32) {
180 unsafe {
181 self.hwnd().SendMessage(em::SetSel { start, end });
182 }
183 }
184
185 /// Sets the text by calling
186 /// [`HWND::SetWindowText`](crate::prelude::user_Hwnd::SetWindowText).
187 pub fn set_text(&self, text: &str) -> SysResult<()> {
188 self.hwnd().SetWindowText(text)?;
189 Ok(())
190 }
191
192 /// Displays a balloon tip by sending an
193 /// [`em::ShowBalloonTip`](crate::msg::em::ShowBalloonTip) message.
194 pub fn show_ballon_tip(&self, title: &str, text: &str, icon: co::TTI) -> SysResult<()> {
195 let mut title16 = WString::from_str(title);
196 let mut text16 = WString::from_str(text);
197
198 let mut info = EDITBALLOONTIP::default();
199 info.set_pszTitle(Some(&mut title16));
200 info.set_pszText(Some(&mut text16));
201 info.ttiIcon = icon;
202
203 unsafe { self.hwnd().SendMessage(em::ShowBalloonTip { info: &info }) }
204 }
205
206 /// Retrieves the text by calling
207 /// [`HWND::GetWindowText`](crate::prelude::user_Hwnd::GetWindowText).
208 #[must_use]
209 pub fn text(&self) -> SysResult<String> {
210 self.hwnd().GetWindowText()
211 }
212}
213
214/// Options to create an [`Edit`](crate::gui::Edit) programmatically with
215/// [`Edit::new`](crate::gui::Edit::new).
216pub struct EditOpts {
217 /// Text of the control to be
218 /// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
219 ///
220 /// Defaults to empty string.
221 pub text: String,
222 /// Left and top position coordinates of control within parent's client
223 /// area, to be
224 /// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
225 ///
226 /// Defaults to `gui::dpi(0, 0)`.
227 pub position: (i32, i32),
228 /// Control width to be
229 /// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
230 ///
231 /// Defaults to `gui::dpi_x(100)`.
232 pub width: i32,
233 /// Control height to be
234 /// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
235 ///
236 /// Defaults to `gui::dpi_y(23)`.
237 ///
238 /// **Note:** You should change the default height only in a multi-line
239 /// edit, otherwise it will look off.
240 pub height: i32,
241 /// Edit styles to be
242 /// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
243 ///
244 /// Defaults to `ES::AUTOHSCROLL | ES::NOHIDESEL`.
245 ///
246 /// Suggestions:
247 /// * add `ES::PASSWORD` for a password input;
248 /// * add `ES::NUMBER` to accept only numbers;
249 /// * replace with `ES::MULTILINE | ES::WANTRETURN | ES::AUTOVSCROLL | ES::NOHIDESEL` for a multi-line edit.
250 pub control_style: co::ES,
251 /// Window styles to be
252 /// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
253 ///
254 /// Defaults to `WS::CHILD | WS::GROUP | WS::TABSTOP | WS::VISIBLE`.
255 pub window_style: co::WS,
256 /// Extended window styles to be
257 /// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
258 ///
259 /// Defaults to `WS_EX::LEFT | WS_EX::CLIENTEDGE`.
260 pub window_ex_style: co::WS_EX,
261
262 /// The control ID.
263 ///
264 /// Defaults to an auto-generated ID.
265 pub ctrl_id: u16,
266 /// Horizontal and vertical behavior of the control when the parent window
267 /// is resized.
268 ///
269 /// **Note:** You should use `Vert::Resize` only in a multi-line edit.
270 ///
271 /// Defaults to `(gui::Horz::None, gui::Vert::None)`.
272 pub resize_behavior: (Horz, Vert),
273}
274
275impl Default for EditOpts {
276 fn default() -> Self {
277 Self {
278 text: "".to_owned(),
279 position: dpi(0, 0),
280 width: dpi_x(100),
281 height: dpi_y(23),
282 control_style: co::ES::AUTOHSCROLL | co::ES::NOHIDESEL,
283 window_style: co::WS::CHILD | co::WS::GROUP | co::WS::TABSTOP | co::WS::VISIBLE,
284 window_ex_style: co::WS_EX::LEFT | co::WS_EX::CLIENTEDGE,
285 ctrl_id: 0,
286 resize_behavior: (Horz::None, Vert::None),
287 }
288 }
289}